\(\qquad\)A lo largo de la historia de Chile han pasado un sin número de presidentes por la casa de gobierno, siendo tanto de izquierda, derecha, centro o de algún partido que comparte más de una posición. Si bien, son de diferentes líneas ideológicas, algo que tienen en común estos gobiernos es como llegan al poder, que es mediante las promesas que realizan a lo largo de su candidatura al pueblo, y que en la mayoría de las veces estas quedan sin cumplir. Un claro ejemplo de esto son los gobiernos de Sebastián Piñera I (primer mandato) y Michelle Bachelet II (segundo mandato), llegando a alcanzar un cumplimiento de promesas de un 54% y 56% en su cuarto año de mandato respectivamente.
\(\qquad\)Así, en el presente informe se pretende indagar la hipótesis de que si hay una relación existente entre los discursos que realizan los presidentes, en una primera instancia específicamente los referentes a Sebastián Piñera I y Michelle Bachelet II, y el progreso de cumplimiento de sus promesas realizadas, abarcando las diferentes áreas a las que estas competen (como salud, educación, transporte, etc). Además, se pretende contrastar esta información
\(\qquad\)Comenzando con la adquisición de datos, se siguieron las instrucciones del repositorio de Jorge Pérez, descargando los discursos correspondientes a los presidentes Salvador Allende, Sebastián Piñera y Michelle Bachelet. Los discursos se encontraban en formato .htm, separados según la fecha de emisión.
\(\qquad\)Una vez guardados los archivos, se procede a examinar los archivo de los presidentes, para observar la estructura en la que fueron guardados. Se tiene que los archivos correspondiente a discursos de Sebastián Piñera y Michelle Bachelet tienen formato de página web, mientras que los archivos correspondientes a Salvador Allende se encuentran en un formato tipo texto de noticia, con título, bajada y cuerpo.
\(\qquad\)Para lograr uniformidad en cuanto a los archivos, se ejecutó un código en Python obtenido desde el mismo repositorio de Jorge Pérez, el cual utiliza librerías estándar junto a BeautifulSoup4 con lxml. Así, se tiene que los archivos quedan con la siguiente estructura: por un lado, un archivo por discurso, cada uno conteniendo sólo el contenido (cuerpo) del discurso respectivo, y, por otro lado, un archivo que contenga toda la información sobre fechas y títulos asociados a estos.
\(\qquad\)Un análisis preliminar realizado, consistió en contar qué palabras se repetían más en los discursos, tanto de manera general (agrupando todos los presidentes), como de manera específica (es decir, las palabras más dichas por Salvador Allende, Sebastián Piñera y Michelle Bachelet). De esto, se obtuvieron los siguientes gráficos:
## Registered S3 methods overwritten by 'ggplot2':
## method from
## [.quosures rlang
## c.quosures rlang
## print.quosures rlang
\(\qquad\)Las temáticas a abordar son la relación existente entre los discursos realizados por los presidentes y cómo estos se traducen en el cumplimiento de las promesas que hacen; mientras que, por otro lado, queremos saber si existe relación entre los discursos que da un presidente y la aprobación que este recibe. Esta última temática a abordar basará su información de los datos tomados por diferentes entidades encuestadoras, tales como Cadem, CEP, entre otras.
\(\qquad\)Para desarrollar esto, en una primera instancia se obtendrá la cantidad de veces que se repite cada palabra en los discursos, utilizando herramientas desarrolladas por nosotros. Para revisar el cumplimiento de las distintas temáticas, veremos cómo varía la frecuencia de ciertas palabras clave a lo largo del periodo de gobierno en sus distintos discursos.
\(\qquad\)En cuanto a la relación entre los discursos y aprobación, se buscará clasificar un discurso según la aprobación obtenida luego de dar este, utilizando herramientas de minería de datos. Esto con el fin de crear un sistema de predicción acerca de cómo un discurso afectará la aprobación presidencial.
1.- Repositorio de Jorge Pérez (https://github.com/jorgeperezrojas/discursos)
\(\qquad\)Para descargar los datos originales y descomprimirlos (siguiendo la estructura de carpetas esperada) se debe ejecutar los siguientes comandos desde la raíz, cambiando <presidente> por piñera_1, bachelet_2 o allende.
wget http://dcc.uchile.cl/~jperez/resources/<presidente>.tar.gz
tar zxvf <presidente>.tar.gz
\(\qquad\)A continuación, se deben eliminar los archivos que comienzan con "._", que resultan de la descompresión de los discursos descargados. Para extraer el texto crudo desde los archivos .html (o .htm) se debe ejecutar en la raíz (requiere python 3, un par de librerías estándar y BeautifulSoup):
python src/html_to_txt_plus_meta_<presidente>.py
\(\qquad\)Esto también almacena archivos (meta.txt) con algunos metadatos de cada discurso obtenidos desde los .html (o .htm) correspondientes (como fecha, títulos, subtítulos y direcciones de archivos de imágenes para ciertos discursos). Para limpiar los datos de texto, eliminando textos con muy pocos caracteres útiles, eliminar algunos discursos duplicados (o discursos que son realmente extractos de otros), y eliminar párrafos con palabras de baja frecuencia (para borrar partes en idioma no español, por ejemplo) se debe ejecutar:
python src/text_processing.py data/<presidente>/ --duplicateDeletion --frequencyCleaning 0.19 --verbose
2.- Código propio
\(\qquad\)Luego de procesar el texto, se crean diccionarios que llevan la cuenta de cada palabra en cada texto. Para esto se utilizó el siguiente código (JSE8):
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class PresidentCounter {
public static void main(String[] args) throws IOException {
System.out.println(allende().toString());
System.out.println(bachelet().toString());
System.out.println(pinera().toString());
}
public static HashMap<String, Integer> allende() throws IOException {
WordCounter wc = new WordCounter();
File path = new File("data/allende/processed_text/discursos_limpios");
HashMap<String, Integer> final_dict = new HashMap<String, Integer>();
fillDict(wc, path, final_dict);
HashMap<String, Integer> sorted_dict = final_dict.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
return sorted_dict;
}
public static HashMap<String, Integer> bachelet() throws IOException {
WordCounter wc = new WordCounter();
File path = new File("data/bachelet_2/processed_text/discursos_limpios");
HashMap<String, Integer> final_dict = new HashMap<String, Integer>();
fillDict(wc, path, final_dict);
HashMap<String, Integer> sorted_dict = final_dict.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
return sorted_dict;
}
public static HashMap<String, Integer> pinera() throws IOException {
WordCounter wc = new WordCounter();
File path = new File("data/pinera_1/processed_text/discursos_limpios");
HashMap<String, Integer> final_dict = new HashMap<String, Integer>();
fillDict(wc, path, final_dict);
HashMap<String, Integer> sorted_dict = final_dict.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
return sorted_dict;
}
private static void fillDict(WordCounter wc, File path, HashMap<String, Integer> final_dict) throws IOException {
for (File fileEntry : path.listFiles()) {
HashMap<String, Integer> dict = wc.count(path + "/" + fileEntry.getName());
if (final_dict.isEmpty()) {
final_dict.putAll(dict);
} else {
dict.forEach((key, value) -> final_dict.merge(key, value, (v1, v2) -> v1 + v2));
}
}
}
}
\(\qquad\)El cual, a su vez, hace uso del siguiente código, que debe encontrarse en el mismo directorio que el código anterior:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
public class WordCounter {
public HashMap<String, Integer> count(String txt_file_path) throws IOException {
HashMap<String, Integer> dict = new HashMap<String, Integer>();
BufferedReader br = new BufferedReader(new FileReader(txt_file_path));
String line = br.readLine();
while (line != null) {
String[] words = wordsClear(line.split(" "));
String[] conectores = conectores();
for (String word : words) {
if (word.equals("") || Arrays.asList(conectores).contains(word) ||
word.equals("\t") || word.equals("\r") || word.equals("\n")) {
continue;
}
if (dict.isEmpty() || !dict.containsKey(word)) {
dict.put(word, 0);
}
dict.put(word, dict.get(word) + 1);
}
line = br.readLine();
}
return dict;
}
private String[] wordsClear(String[] array) {
int len = array.length;
String regex = "[a-zñáéíóúü]";
for (int i = 0; i < len; i++) {
String word = array[i].toLowerCase();
String[] letters = word.split("");
StringBuilder new_word = new StringBuilder();
for (String letter : letters) {
if (letter.matches(regex)) {
new_word.append(letter);
}
}
array[i] = new_word.toString();
}
return array;
}
// Algunas palabras a omitir
private String[] conectores() {
StringBuilder sb = new StringBuilder();
sb.append("de que la y el en a los del las no un por se para es con una lo al su más como ha o pero este ");
sb.append("porque hay han hemos eso si tiene son sus yo esta nosotros me también cuando nuestra he está ");
sb.append("sobre ser nuestro tienen hacer aquí ustedes todos sin nos qué sólo le tenemos muy les mi hecho ");
sb.append("todo año ellos gran ya desde ni puede están esto entre debe contra cada sino va hoy día nuestros además ");
sb.append("así esa solamente quiero ese años estamos vamos tanto mil mejor donde uno importante forma tener poder ");
sb.append("muchas van entonces mucho todas ");
return sb.toString().split(" ");
}
}
\(\qquad\)Para que este código funcione debe seguirse la siguiente estructura:
.
├── data
│ ├── allende
│ │ └── processed_text
│ │ └── discursos_limpios
│ ├── bachelet_2
│ │ └── processed_text
│ │ └── discursos_limpios
│ └── piñera_1
│ └── processed_text
│ └── discursos_limpios
└── src
└── main
└── java
└── processing
├── WordCounter.java
└── PresidentCounter.java
\(\qquad\)El trabajo realizado para el Hito 1 se enfocó principalmente en un análisis exploratorio de los datos recabados hasta el momento, los cuales correspondían a discursos presidenciales durante el gobierno de Salvador Allende, Sebastián Piñera (primer mandato) y Michelle Bachelet (segundo mandato). Estos discursos debieron ser procesados debidamente antes de ser utilizados para algún análisis, ya que se encontraban en formato de noticia, html y no todos poseían el mismo formato, por lo que se debe utilizar un código para dejarlos en texto plano. Luego, se planteó una hipótesis a comprobar, la cuál es que “existe relación entre los discursos que realizan los presidentes y el progreso de cumplimiento de sus promesas realizadas. Finalmente, se hizo una nube de palabras para ver cuales eran las más nombradas por los presidentes, resultando lo siguiente:
\(\qquad\)Así, tomando en cuenta lo anteriormente nombrado, en el presente informe se pretende corroborar en parte la hipótesis propuesta, a través de la incorporación de más información y el análisis de estos datos.
\(\qquad\)En el Hito 1 se había pre procesado la información correspondiente a los discursos presidenciales de Salvador Allende, Sebastián Piñera I y Michelle Bachelet II. Para complementar ésta información, se pretendía utilizar los índices de aprobación de los gobiernos de Piñera y Bachelet, pero hubo una traba con la obtención de información, ya que la organización que realizaba la encuesta cambió su sitio web, quedando inaccesibles los archivos. Para solucionar esto, se indaga sobre diferentes páginas y se llegó a “WayBackMachine” (http://web.archive.org/), que entrega el contenido de alguna url ingresada según una fecha seleccionada, pudiendo ingresar a los informes de “Evaluación del Gobierno” y obtener los índices de aprobación y desaprobación, los que fueron transcritos a mano en un excel. Estos fueron clasificados en clases según su porcentaje de aprobación como se muestra a continuación:
Aprobación | Clase |
---|---|
[0%, 10%[ | 0 |
[10%, 20%[ | 1 |
[20%, 30%[ | 2 |
[30%, 40%[ | 3 |
[40%, 50%[ | 4 |
[50%, 60%[ | 5 |
[60%, 70%[ | 6 |
[70%, 80%[ | 7 |
[80%, 90%[ | 8 |
[90%, 100%[ | 9 |
\(\qquad\)Por otro lado, se propuso incorporar los discursos de los últimos presidentes argentinos al proyecto, para contar una base de datos más robusta. Por ello, se obtuvieron los “índices de confianza en el gobierno” de los presidentes Néstor Kirchner, Cristina Fernández (ambos gobiernos) y Mauricio Macri. Estos índices se obtuvieron desde la página de la Universidad Torcuato Di Tella, que es la encargada de realizar esta medición. El índice recabado se mide en una escala del 0 al 5, siendo el 0 la menor puntuación y 5 la mayor. Para efectos del proyecto, se extrapola el resultado dado, por ejemplo, si se obtuvo 2,5 de índice de confianza, se traduce a un 50% de aprobación. Sin embargo, estos datos no fueron utilizados en los experimentos de este Hito, ya que se tuvieron problemas para poder obtener los discursos de estos presidentes, pero si se utilizarán en el Hito 3.
\(\qquad\)Realizando un análisis de los datos incorporados, se tiene la aprobación promedio de los presidentes son las siguientes:
Presidente | Aprobación promedio |
---|---|
Piñera I | 37% |
Bachelet II | 28% |
Kirchner | 38% |
Fernández | 35% |
Macri | 47% |
\(\qquad\)De estos se tiene que Michelle Bachelet obtuvo la peor aprobación de todos los presidentes con los que se trabajarán, mientras que Mauricio Macri obtuvo la mejor. Por otro lado, se pudo visualizar en los datos que los presidentes chilenos contaban con sus índices de aprobación de manera más extrema, es decir, sus índices eran buenos o malos, no intermedios, mientras que en el caso de los presidentes argentinos sus índices de aprobación se dió de manera más centrada.
\(\qquad\)Para experimentar con la clasificación de los discursos, se otorgó una clase a cada discurso según la fecha en la que este se dió y la aprobación que se tenía en ese mes. Luego, utilizando la librería de python “scikit-learn” con el módulo CountVectorizer para procesar texto. En particular, utilizando una lista de stop words en español obtenidas de éste repositorio, se utilizó CountVectorizer para encontrar un vocabulario que contenga todas las palabras mencionados a lo largo de los mandatos de Piñera y Bachelet.
\(\qquad\)Con este vocabulario se procedió a contar la frecuencia de cada palabra en cada discurso en particular (nuevamente utilizando CountVectorizer), y con esto obtener los atributos de cada discurso. Finalmente, se probaron distintos clasificadores para testear: en particular, árbol de decisiones, support vector machine y naive Bayes. Al realizar los experimentos se puede notar que las clases 6 a 9 no son utilizadas puesto que ningún mandatario ha llegado a una aprobación tan alta. Los resultados de este experimento se detallan en la siguiente sección.
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
0 | 0.08 | 0.09 | 0.08 | 58 |
1 | 0.19 | 0.18 | 0.19 | 119 |
2 | 0.44 | 0.49 | 0.46 | 254 |
3 | 0.46 | 0.46 | 0.46 | 139 |
4 | 0.17 | 0.14 | 0.16 | 106 |
5 | 0.11 | 0.04 | 0.06 | 26 |
Micro Avg | 0.33 | 0.33 | 0.33 | 702 |
Macro Avg | 0.24 | 0.23 | 0.23 | 702 |
Weighted Avg | 0.32 | 0.33 | 0.32 | 702 |
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
0 | 1.00 | 0.02 | 0.03 | 58 |
1 | 0.31 | 0.09 | 0.14 | 119 |
2 | 0.45 | 0.89 | 0.59 | 254 |
3 | 0.67 | 0.53 | 0.59 | 139 |
4 | 0.33 | 0.16 | 0.22 | 106 |
5 | 0.00 | 0.00 | 0.00 | 26 |
Micro Avg | 0.47 | 0.47 | 0.47 | 702 |
Macro Avg | 0.46 | 0.28 | 0.26 | 702 |
Weighted Avg | 0.48 | 0.47 | 0.39 | 702 |
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
0 | 0.00 | 0.00 | 0.00 | 58 |
1 | 0.00 | 0.00 | 0.00 | 119 |
2 | 0.45 | 0.95 | 0.62 | 254 |
3 | 0.58 | 0.67 | 0.62 | 139 |
4 | 0.70 | 0.07 | 0.12 | 106 |
5 | 0.00 | 0.00 | 0.00 | 26 |
Micro Avg | 0.49 | 0.49 | 0.49 | 702 |
Macro Avg | 0.29 | 0.28 | 0.23 | 702 |
Weighted Avg | 0.39 | 0.49 | 0.36 | 702 |
\(\qquad\)Considerando los comentarios realizados en el primero hito, decidimos dar énfasis en la relación entre discursos y aprobación de un presidente. Además, con esto podemos tomar en consideración los presidentes argentinos, los cuales habían sido separados de los primeros experimentos puesto que no se cuenta con la información sobre cumplimiento de promesas.
\(\qquad\)Si bien los resultados obtenidos no son muy buenos en relación a lo esperado (sobre 70% de precisión), aún hay que realizar experimentos con más clasificadores y con los discursos de los presidentes argentinos. Además, se pueden probar con distintas clasificaciones para comparar los modelos.
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score
from sklearn import svm
import os
import pandas as pd
def stop_words_list():
stop_words = open('stopwords-es.txt', 'r', encoding='utf-8')
stop_words = stop_words.readlines()
stop = []
for line in stop_words:
line = line.strip()
stop.append(line)
return stop
bachelet = "bachelet_2/processed_text/discursos_limpios/"
pinera = "pinera_1/processed_text/discursos_limpios/"
files_bachelet = os.listdir(bachelet)
files_pinera = os.listdir(pinera)
discursos = []
for file in files_bachelet:
if (file != ".ipynb_checkpoints"):
discursos.append(bachelet + file)
for file in files_pinera:
if (file != ".ipynb_checkpoints"):
discursos.append(pinera + file)
stop_words = stop_words_list()
vectorizer = CountVectorizer(input='filename', stop_words=stop_words, strip_accents='unicode')
vectorizer.fit_transform(discursos)
vocabulary = vectorizer.get_feature_names()
aprobaciones_bachelet = pd.read_csv('Bachelet.csv', sep=';', encoding='utf-8')
aprobaciones_pinera = pd.read_csv('Pinera.csv', sep=';', encoding='utf-8')
clases = []
for file in files_bachelet:
if (file != ".ipynb_checkpoints"):
year = int(file[0:4])
month = int(file[5:7])
for i in range(len(aprobaciones_bachelet)):
if int(aprobaciones_bachelet.iloc[i]['Anio']) == year and int(aprobaciones_bachelet.iloc[i]['Mes']) == month:
clases.append(aprobaciones_bachelet.iloc[i]['Clase A'])
break
for file in files_pinera:
if (file != ".ipynb_checkpoints"):
year = int(file[0:4])
month = int(file[5:7])
for i in range(len(aprobaciones_pinera)):
if int(aprobaciones_pinera.iloc[i]['Anio']) == year and int(aprobaciones_pinera.iloc[i]['Mes']) == month:
clases.append(aprobaciones_pinera.iloc[i]['Clase A'])
break
atributos = []
for file in files_bachelet:
if (file != ".ipynb_checkpoints"):
un_discurso = [bachelet + file]
vectorizer2 = CountVectorizer(input='filename', stop_words=stop_words, vocabulary=vocabulary, strip_accents='unicode')
X = vectorizer2.fit_transform(un_discurso)
atributos.append(X.toarray()[0])
for file in files_pinera:
if (file != ".ipynb_checkpoints"):
un_discurso = [pinera + file]
vectorizer2 = CountVectorizer(input='filename', stop_words=stop_words, vocabulary=vocabulary, strip_accents='unicode')
X = vectorizer2.fit_transform(un_discurso)
atributos.append(X.toarray()[0])
X_train, X_test, y_train, y_test = train_test_split(atributos,
clases,
test_size=.33,
stratify=clases)
clf_tree = DecisionTreeClassifier()
clf_tree.fit(X_train, y_train)
y_pred_1 = clf_tree.predict(X_test)
clf_gnb = GaussianNB()
clf_gnb.fit(X_train, y_train)
y_pred_2 = clf_gnb.predict(X_test)
clf_svm = svm.SVC(gamma='scale')
clf_svm.fit(X_train, y_train)
y_pred_3 = clf_svm.predict(X_test)
print("Accuracy tree:", accuracy_score(y_test, y_pred_1))
print(classification_report(y_test, y_pred_1))
print("Accuracy gaussian:", accuracy_score(y_test, y_pred_2))
print(classification_report(y_test, y_pred_2))
print("Accuracy SVC:", accuracy_score(y_test, y_pred_3))
print(classification_report(y_test, y_pred_3))
\(\qquad\)El trabajo realizado para el Hito 2 se enfocó principalmente en experimentar con los datos obtenidos del Hito 1, probando diferentes modelos con el fin de analizar las diferentes métricas que estos entregaban, tales como accuracy, recall o f1 score, y ver cuáles de estos modelos podrían ser utilizados para el tratamiento final de los datos, para lograr recabar información de estos.
\(\qquad\)Para poder realizar esto, se utilizaron las mismas herramientas utilizadas en el Hito 2, el algoritmo de “Bag of words” y el paquete de “Stop words” en español. Se realizaron 3 experimentos iniciales, los cuales fueron:
\(\qquad\)Para el análisis de estos modelos se observó principalmente el Accuracy que tenían, siendo el mejor modelo el realizado con Support Vector Machine.
\(\qquad\)Para la realización del Hito 2 se obtuvieron los índices de aprobación de los presidentes chilenos (primer gobierno de Sebastián Piñera y segundo de Michelle Bachelet) y se trataron, quedando pendiente la obtención de los datos de los presidentes argentinos. Así, para este hito se obtuvieron los datos del índice de confianza de gobierno de argentina para los presidentes de este país (primer gobierno de Néstor Kirchner, ambos gobiernos de Cristina Fernández y los datos del actual gobierno de Mauricio Macri) que son el equivalente al índice de aprobación chileno, salvo que este índice está en una escala de 0 a 5, por lo que se debió transformar este índice a un porcentaje, para mantener la coherencia y consistencia de los datos con los que se trabaja. Además, se obtuvieron otros datos que son relevantes para la explicación del índice de aprobación, tales como el IPC mensual y la tasa de desempleo durante cada mandato, obtenidos, en el caso de Chile, de la base de datos del banco central y el INE, y en el caso de Argentina, de la base de datos del INDEC.
\(\qquad\)Una vez obtenidos estos datos, se obtuvieron los discursos de los presidentes argentinos, y se clasificó cada uno de estos en 5 temas predominantes, para finalmente unir estos discursos clasificados con los datos recabados anteriormente, quedando una base de datos de los discursos como se muestra en la siguiente tabla.
Año | Mes | Presidente | Tema1 | Tema2 | Tema3 | Tema4 | Tema5 | Desempleo | IPC | Aprobación |
---|
\(\qquad\)Tomando en cuenta el feedback entregado, se realizaron diversos cambios a los experimentos. Lo primero fue particionar los discursos antes de obtener el vocabulario, de forma que el vocabulario utilizado sea solo aquel obtenido desde el set de entrenamiento.
\(\qquad\)El primer experimento para este hito consistió en realizar un clasificador dummy que clasificará todos los discursos en una sola clase, esto con el fin de poder compararlo con los demás clasificadores. Además, por los resultados obtenidos en la clasificación del hito anterior y los comentarios entregados, se decidió utilizar Multinomial Naive Bayes y Linear Support Vector Machine.
\(\qquad\)Finalmente, se realizaron 2 regresiones tomando en cuenta el IPC, tasa de desempleo y los 5 temas más repetidos en los discursos de cada mes. Para realizar esto último, se realizó un vocabulario para distintos temas con palabras clave en cada uno de estos. Cada discurso tiene 1 o más temas, donde se decidió que un discurso trataba sobre un tema si se encontraban al menos 7 palabras relacionadas a dicho tema.
\(\qquad\)Para el clasificador dummy se obtuvo una precisión de 0.08, clasificando todos los discursos en la clase 1. En la siguiente tabla se pueden observar distintas métricas:
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
1 | 0.08 | 1.00 | 0.15 | 102 |
2 | 0.00 | 0.00 | 0.00 | 373 |
3 | 0.00 | 0.00 | 0.00 | 384 |
4 | 0.00 | 0.00 | 0.00 | 219 |
5 | 0.00 | 0.00 | 0.00 | 160 |
6 | 0.00 | 0.00 | 0.00 | 10 |
Micro Avg | 0.08 | 0.08 | 0.08 | 1248 |
Macro Avg | 0.01 | 0.17 | 0.03 | 1248 |
Weighted Avg | 0.01 | 0.08 | 0.01 | 1248 |
\(\qquad\)Además se obtuvo la siguiente matriz de confusión:
Clase 1 | Clase 2 | Clase 3 | Clase 4 | Clase 5 | Clase 6 | |
---|---|---|---|---|---|---|
Clase 1 | 102 | 0 | 0 | 0 | 0 | 0 |
Clase 2 | 373 | 0 | 0 | 0 | 0 | 0 |
Clase 3 | 384 | 0 | 0 | 0 | 0 | 0 |
Clase 4 | 219 | 0 | 0 | 0 | 0 | 0 |
Clase 5 | 160 | 0 | 0 | 0 | 0 | 0 |
Clase 6 | 10 | 0 | 0 | 0 | 0 | 0 |
\(\qquad\)Para el clasificador naive bayes se obtuvo una precisión de 0.42. En la siguiente tabla se pueden observar distintas métricas:
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
1 | 0.25 | 0.87 | 0.39 | 102 |
2 | 0.42 | 0.10 | 0.16 | 373 |
3 | 0.56 | 0.59 | 0.57 | 384 |
4 | 0.33 | 0.39 | 0.36 | 219 |
5 | 0.68 | 0.57 | 0.62 | 160 |
6 | 0.22 | 0.20 | 0.21 | 10 |
Micro Avg | 0.42 | 0.42 | 0.42 | 1248 |
Macro Avg | 0.41 | 0.45 | 0.38 | 1248 |
Weighted Avg | 0.46 | 0.42 | 0.40 | 1248 |
Clase 1 | Clase 2 | Clase 3 | Clase 4 | Clase 5 | Clase 6 | |
---|---|---|---|---|---|---|
Clase 1 | 89 | 8 | 0 | 1 | 4 | 0 |
Clase 2 | 175 | 36 | 99 | 61 | 0 | 2 |
Clase 3 | 52 | 24 | 225 | 77 | 6 | 0 |
Clase 4 | 27 | 12 | 65 | 85 | 28 | 2 |
Clase 5 | 14 | 6 | 13 | 32 | 92 | 3 |
Clase 6 | 0 | 0 | 0 | 2 | 6 | 2 |
\(\qquad\)Para el clasificador support vector machine se obtuvo una precisión de 0.62. En la siguiente tabla se pueden observar distintas métricas:
Precision | Recall | F1-score | Support | |
---|---|---|---|---|
1 | 0.46 | 0.48 | 0.47 | 102 |
2 | 0.61 | 0.65 | 0.63 | 373 |
3 | 0.69 | 0.72 | 0.71 | 384 |
4 | 0.55 | 0.45 | 0.49 | 219 |
5 | 0.65 | 0.64 | 0.64 | 160 |
6 | 0.50 | 0.20 | 0.29 | 10 |
Micro Avg | 0.62 | 0.62 | 0.62 | 1248 |
Macro Avg | 0.58 | 0.52 | 0.54 | 1248 |
Weighted Avg | 0.62 | 0.62 | 0.62 | 1248 |
\(\qquad\)Además se obtuvo la siguiente matriz de confusión:
Clase 1 | Clase 2 | Clase 3 | Clase 4 | Clase 5 | Clase 6 | |
---|---|---|---|---|---|---|
Clase 1 | 49 | 47 | 2 | 2 | 2 | 0 |
Clase 2 | 46 | 244 | 50 | 27 | 6 | 0 |
Clase 3 | 5 | 64 | 277 | 28 | 10 | 0 |
Clase 4 | 5 | 33 | 53 | 98 | 30 | 0 |
Clase 5 | 2 | 13 | 17 | 24 | 102 | 2 |
Clase 6 | 0 | 0 | 1 | 0 | 7 | 2 |
\(\qquad\)Este último clasificador fue el que mejor resultados entregó, incluso considerando las regresiones que se mostrarán más adelante. De la matriz de confusión, podemos observar que la mayoría de los discursos que pertenecen a una clase n
y son clasificados incorrectamente, son clasificados en la clase n - 1
o n + 1
. Esto quiere decir que nuestro clasificador, cuando falla en clasificar, lo hace estando cerca del resultado real. Cabe destacar que un discurso clasificado en una clase vecina puede tener hasta un 20% de error, el cual corresponde a la máxima diferencia de aprobación posible entre dos clases consecutivas.
\(\qquad\)Para las regresiones se realizaron 2 modelos, uno sin considerar las temáticas de los discursos y otros considerando las temáticas de los discursos, con el fin de analizar la calidad de los datos que estamos utilizando y ver si estos realmente están aportando al modelo.
\(\qquad\)Así, se tiene que el primer modelo de regresión lineal es como se muestra a continuación: Aprobación ~ IPC + Año + Mes + Presidente + Desempleo
\(\qquad\)De esto, se tiene que la información más relevante se muestra en la tabla siguiente tabla:
Estimate | Std. Error | t-value | Pr(> | t | |
---|---|---|---|---|---|
PresidenteFernandez | 0.0173 | 0.0094 | 1.829 | 0.0674 | . |
PresidenteKrichner | NA | NA | NA | NA | |
PresidenteMacri | 0.3382 | 0.1168 | 28.95 | <2e-16 | *** |
PresidentePiñera | -0.042 | 0.0102 | -4.203 | 2.68e-15 | *** |
Desempleo | -0.6397 | 0.3658 | -1.748 | 0.0804 | . |
R-square | 0.4967 | Adjusted | R-squared | 0.4935 |
\(\qquad\)Des este primer modelo se tiene que el presidente omitido es Michelle Bachelet, por lo que con una significancia de 0.1 se tiene que un gobierno será mejor evaluado si tiene en la cabeza a Cristina Fernández o Mauricio Macri, mientras que será peor evaluado si tiene a Sebastián Piñera. Además, se tiene que el desempleo será un factor que afecte de manera negativa a cualquier modelo, por lo que si aumenta en un 1%, el índice de aprobación se verá afectado en -0.639%. Por último, se tiene que el R cuadrado del modelo es 0.4935, lo cual indica que no es muy buen modelo, ya que mientras más cercano a 1 es mejor, y al ser menor que 0.5 indica que no se explica muy bien la aprobación con las variables seleccionadas, y que el R cuadrado ajustado es muy similar al normal, por lo que nos está diciendo que no se está penalizando el modelo por los datos que se usaron, en otras palabras, no se incorporaron datos “basuras” o que no aportan al modelo.
\(\qquad\)En cuanto al segundo modelo, se tiene que se incorporaron las temáticas de los discursos a la regresión, quedando de la siguiente forma: Aprobación ~ IPC + Año + Mes + Presidente + Desempleo + tema1 + tema2 + tema3 + tema4 + tema5
\(\qquad\)De esto, se tiene que la información más relevante se muestra en la tabla siguiente tabla:
Estimate | Std. Error | t-value | Pr(> | t | |
---|---|---|---|---|---|
PresidenteFernandez | 0.0390 | 0.011 | 3.496 | 0.000 | *** |
PresidenteKrichner | NA | NA | NA | NA | |
PresidenteMacri | 0.4003 | 0.0192 | 20.772 | <2e-16 | *** |
PresidentePiñera | -0.0350 | 0.0114 | -3.068 | 0.0021 | ** |
tema1deportes | -0.0559 | 0.0288 | -1.940 | 0.0525 | . |
tema1desarrollo_social | -0.0285 | 0.0131 | -2.175 | 0.0297 | * |
tema1economia_y_comercio | -0.0380 | 0.0204 | -1.861 | 0.0628 | . |
R-square | 0.5017 | Adjusted | R-squared | 0.4843 |
\(\qquad\)Al igual que en el modelo anterior, Michelle Bachelet fue la presiente omitida, por lo que se mantiene la evaluación de los gobiernos según quien esté en la cabeza. Por otro lado, ninguno de los temas resultó significativo salvo deportes, desarrollo social y economía y comercio, siendo el tema omitido justicia, por lo que este debería ser el tema que tiene más relevancia en la ciudadanía a la hora de evaluar a los presidentes, ya que estos temas son negativos. Por último, el R cuadrado aumentó ligeramente, significando que el modelo de explicación de la aprobación aumentó, sin embargo el R cuadrado ajustado disminuyó, por lo que nos indica que se penaliza por los datos, es decir, se ingresaron muchos datos basura que no aportaron al modelo.
\(\qquad\)A pesar de que la clasificación no es el mejor método para resolver el problema que nos planteamos, obtuvimos mejores resultados que utilizando regresiones. Parte de esto se debe a que la aprobación de un presidente no está sujeta únicamente a los discurso que da, si no que dependen del contexto en el que se emitan, razón por la cual decidimos agregar algunos indicadores (IPC y tasa de desempleo).
\(\qquad\)Además, por la forma en la que abordamos el problema y la inclusión de los temas tratados en los discursos, no logramos observar una relación entre lo que se dice en un discurso y el cumplimiento de las promesas. Más aún, observamos que los temas de los discursos no afectan significativamente la aprobación.
\(\qquad\)Para finalizar, creemos que se pueden obtener mejores resultados cambiando algunas cosas. Primero, agrupar todos los discursos por mes, sumando los vectores obtenidos en el algoritmo bag of words. La razón de esto es que un discurso no afecta por sí solo la aprobación de ese mes, si no que lo hace en conjunto a todos los discursos dados dicho mes. Además, esto nos entregaría consistencia con los indicadores mensuales que utilizamos. De esta manera, reduciríamos la cantidad de datos a clasificar, pues se intentaría predecir la aprobación de un mes en lugar de la de un discurso.
\(\qquad\)Realizando los cambios mencionados anteriormente se podrían incluir las palabras como atributos en las regresiones y obtener algún resultado con sentido. Esto se intentó hacer en las regresiones mostradas, pero las aprobaciones entregadas eran números muy grandes o muy negativos (valores totalmente alejados del rango [0, 1] que buscábamos) y tenían un error del mismo orden.
#%%
from sklearn.feature_extraction.text import CountVectorizer
import os
from random import random
def stop_words_list():
stop_words = open('stopwords-es.txt', 'r', encoding='utf-8')
stop_words = stop_words.readlines()
stop = []
for line in stop_words:
line = line.strip()
stop.append(line)
return stop
bachelet = "presidentes/bachelet_2/discursos_limpios/"
pinera = "presidentes/pinera_1/discursos_limpios/"
fernandez = "presidentes/fernandez/discursos_limpios/"
kirchner = "presidentes/kirchner/discursos_limpios/"
macri = "presidentes/macri/discursos_limpios/"
presidentes = [bachelet, pinera, fernandez, kirchner, macri]
min_prob = 0.3
discursos_train, discursos_test = [[], []]
for presidente in presidentes:
files_dir = os.listdir(presidente)
for file in files_dir:
if file != ".ipynb_checkpoints":
prob = random()
discurso = presidente + file
if prob < min_prob:
discursos_test.append(discurso)
else:
discursos_train.append(discurso)
stop_words = stop_words_list()
vectorizer = CountVectorizer(input='filename',
stop_words=stop_words,
strip_accents='unicode')
vectorizer.fit_transform(discursos_train)
vocabulary = vectorizer.get_feature_names()
print("Vocabulario cargado",
(len(discursos_train) * 1.0) / (len(discursos_train) + len(discursos_test)),
(len(discursos_test) * 1.0) / (len(discursos_train) + len(discursos_test)))
#Liberar memoria
del vectorizer
del min_prob
#%%
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from imblearn.over_sampling import RandomOverSampler
aprobacion_bachelet = pd.read_csv('presidentes/bachelet_2/bachelet_2.csv', sep=';', encoding='utf-8')
aprobacion_pinera = pd.read_csv('presidentes/pinera_1/pinera_1.csv', sep=';', encoding='utf-8')
aprobacion_fernandez = pd.read_csv('presidentes/fernandez/fernandez.csv', sep=';', encoding='utf-8')
aprobacion_kirchner = pd.read_csv('presidentes/kirchner/kirchner.csv', sep=';', encoding='utf-8')
aprobacion_macri = pd.read_csv('presidentes/macri/macri.csv', sep=';', encoding='utf-8')
aprobacion_presidentes = [aprobacion_bachelet, aprobacion_pinera,
aprobacion_fernandez, aprobacion_kirchner, aprobacion_macri]
zipped_presidentes = zip(presidentes, aprobacion_presidentes)
y_train_a, y_test_a, y_train_b, y_test_b = [[], [], [], []]
X_train, X_test = [[], []]
for presidente, aprobacion in zipped_presidentes:
files_dir = os.listdir(presidente)
for file in files_dir:
if file != ".ipynb_checkpoints":
year = int(file[0:4])
month = (int(file[5:7]) + 1) % 12
discurso = presidente + file
for i in range(len(aprobacion)):
if int(aprobacion.iloc[i]['Anio']) == year and int(aprobacion.iloc[i]['Mes']) == month:
clase_a = int(aprobacion.iloc[i]['Clase A'])
clase_b = float(aprobacion.iloc[i]['Clase B'])
if clase_a != 0 and clase_b != 0.0:
vectorizer2 = TfidfVectorizer(input='filename',
stop_words=stop_words,
vocabulary=vocabulary,
strip_accents='unicode')
X = vectorizer2.fit_transform([discurso])
if discurso in discursos_train:
y_train_a.append(clase_a)
y_train_b.append(clase_b)
X_train.append(X.toarray()[0])
else:
y_test_a.append(clase_a)
y_test_b.append(clase_b)
X_test.append(X.toarray()[0])
break
print("Clases cargadas")
print("Atributos cargados")
#Liberar memoria
del discursos_test, discursos_train
del aprobacion_bachelet, aprobacion_pinera
del aprobacion_fernandez, aprobacion_kirchner, aprobacion_macri
del aprobacion_presidentes
del X_train, y_train_a, y_train_b
del ros
#%%
from sklearn.dummy import DummyClassifier
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
clf_dummy = DummyClassifier(strategy='most_frequent')
clf_dummy.fit(X_a, y_a)
y_pred_0 = clf_dummy.predict(X_test)
print("DUMMY")
print("Accuracy:", accuracy_score(y_test_a, y_pred_0))
print(classification_report(y_test_a, y_pred_0))
print(confusion_matrix(y_test_a, y_pred_0), '\n')
#%%
from sklearn.naive_bayes import MultinomialNB
clf_mnb = MultinomialNB()
clf_mnb.fit(X_a, y_a)
y_pred_1 = clf_mnb.predict(X_test)
print("MULTINOMIAL NAIVE BAYES")
print("Accuracy:", accuracy_score(y_test_a, y_pred_1))
print(classification_report(y_test_a, y_pred_1))
print(confusion_matrix(y_test_a, y_pred_1), '\n')
#%%
from sklearn.svm import LinearSVC
clf_svm = LinearSVC()
clf_svm.fit(X_a, y_a)
y_pred_2 = clf_svm.predict(X_test)
print("LINEAR SVM")
print("Accuracy:", accuracy_score(y_test_a, y_pred_2))
print(classification_report(y_test_a, y_pred_2))
print(confusion_matrix(y_test_a, y_pred_2), '\n')
#%%
# CODIGO DE REGRESIONES NO UTILIZADO
‘’’
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.linear_model import LinearRegression
import numpy as np
regressor_l = LinearRegression()
regressor_l.fit(X_b, y_b)
y_pred_r1 = regressor_l.predict(X_test)
print("LINEAR REGRESSION")
print('MAE: ', mean_absolute_error(y_test_b, y_pred_r1))
print('MSE: ', mean_squared_error(y_test_b, y_pred_r1))
print('RMSE:', np.sqrt(mean_squared_error(y_test_b, y_pred_r1)))
print('Score:', regressor_l.score(X_test, y_test_b))
#%%
from sklearn.linear_model import SGDRegressor
regressor_sgd = SGDRegressor(max_iter=1000, tol=1e-3)
regressor_sgd.fit(X_b, y_b)
y_pred_r2 = regressor_sgd.predict(X_test)
print("SGD REGRESSOR")
print('MAE: ', mean_absolute_error(y_test_b, y_pred_r2))
print('MSE: ', mean_squared_error(y_test_b, y_pred_r2))
print('RMSE:', np.sqrt(mean_squared_error(y_test_b, y_pred_r2)))
print('Score:', regressor_sgd.score(X_test, y_test_b))
‘’’